package com.copyctrl.message.config;

import cn.hutool.json.JSONUtil;
import com.copyctrl.campus.common.constant.RedisConstant;
import com.copyctrl.campus.common.domain.LeaveMessages;
import com.copyctrl.campus.common.domain.Message;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;

@Slf4j
@Component
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {

    /**
     * 在线人数
     */
    public static int onlineNumber = 0;
    /**
     * 以用户的id为key，WebSocket为对象保存起来
     */
    private static Map<String, WebSocket> clients = new ConcurrentHashMap<String, WebSocket>();
    /**
     * 会话
     */
    private Session session;
    /**
     * 用户名称
     */
    private String UserId;

    private static StringRedisTemplate redisTemplate;

    /**
     * WebSocket为代码段所在的类的名字
     **/
    @Autowired
    private void setRedisTemplate(StringRedisTemplate stringRedisTemplate) {

        WebSocket.redisTemplate = stringRedisTemplate;

    }

    /**
     * OnOpen 表示有浏览器链接过来的时候被调用
     * OnClose 表示浏览器发出关闭请求的时候被调用
     * OnMessage 表示浏览器发消息的时候被调用
     * OnError 表示有错误发生，比如网络断开了等等
     */

    /**
     * 建立连接
     *
     * @param session
     */
    @OnOpen
    public void onOpen(@PathParam("userId") String userId, Session session) {
        onlineNumber++;
        log.info("现在来连接的客户id：" + session.getId() + "用户id：" + userId);
        this.UserId = userId;
        this.session = session;
        log.info("有新连接加入！ 当前在线人数" + onlineNumber);
        try {
            Map<String, Object> map1 = new HashMap<>();
            //给所有在线好友推送上线信息
            sendMessageAll("好友上线了", userId, "1");
            //把自己的信息加入到map当中去
            clients.put(userId, this);
            //给自己发一条消息：告诉自己现在都有谁在线
            Map<String, Object> map2 = new HashMap<>();
            map2.put("messageType", 3);
            //移除掉自己
            Set<String> set = clients.keySet();
            map2.put("onlineUsers", set);
            sendMessageTo(JSONUtil.toJsonStr(set), userId, "3");
            //TODO 看redis中是否有未读消息，如果有，发送给前端
            getNotReadMessages();
        } catch (IOException e) {
            log.info(userId + "上线的时候通知所有人发生了错误");
        }
    }

    @OnError
    public void onError(Session session, Throwable error) {
        log.info("服务端发生了错误" + error.getMessage());
        //error.printStackTrace();
    }

    /**
     * 连接关闭
     */
    @OnClose
    public void onClose() {
        onlineNumber--;
        //webSockets.remove(this);
        clients.remove(UserId);
        try {
            //messageType 1代表上线 2代表下线 3代表在线名单  4代表普通消息
            Map<String, Object> map1 = new HashMap<>();
            sendMessageAll("有好友下线了", UserId, "2");
        } catch (IOException e) {
            log.info(UserId + "下线的时候通知所有人发生了错误");
        }
        log.info("有连接关闭！ 当前在线人数" + onlineNumber);
    }

    /**
     * 收到客户端的消息
     *
     * @param message 消息
     * @param session 会话
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        try {
            log.info("来自客户端消息：" + message + "客户端的id是：" + session.getId());
            Map jsonObject = JSONUtil.toBean(message, Map.class);
            String textMessage = jsonObject.get("message").toString();
            String tousername = jsonObject.get("to").toString();
            sendMessageTo(textMessage, tousername, "4");
        } catch (Exception e) {
            log.info("发生了错误了");
        }
    }

    public void sendMessageTo(String message, String ToUserId, String type) throws IOException {
        Message m = new Message();
        m.setMessage(message);
        m.setFromId(this.UserId);
        m.setToId(ToUserId);
        m.setMessageType(type);
        m.setTime(new Date());
        if (type.equals("4")) {
            //存入聊天记录里
            Integer i1 = new Integer(UserId);
            Integer i2 = new Integer(ToUserId);
            String key = "";
            if (i1 > i2) {
                key = ToUserId + UserId;
            } else {
                key = UserId + ToUserId;
            }
            redisTemplate.opsForZSet().add(RedisConstant.MESSAGE_ALL + key, JSONUtil.toJsonStr(m), System.currentTimeMillis());
        }
        for (WebSocket item : clients.values()) {
            if (item.UserId.equals(ToUserId)) {
                item.session.getAsyncRemote().sendText(JSONUtil.toJsonStr(m));
                break;
            }
        }
        //把消息存入redis中（未读key）判断类型，仅存type为4的消息
        if (m.getMessageType() == "4") {
            long score = System.currentTimeMillis();
            redisTemplate.opsForZSet().add(RedisConstant.MESSAGE_USER_NOT_READ + m.getToId(), JSONUtil.toJsonStr(m), score);
        }
    }

    public void sendMessageAll(String message, String FromUserId, String type) throws IOException {
        //获取好友列表
        Set<String> members = redisTemplate.opsForSet().members(RedisConstant.USER_FRIEND + FromUserId);
        //两set取交集
        members.retainAll(clients.keySet());
        for (String member : members) {
            WebSocket webSocket = clients.get(member);
            Message m = new Message();
            m.setMessage(message);
            m.setFromId(FromUserId);
            m.setToId(webSocket.UserId);
            m.setMessageType(type);
            webSocket.session.getAsyncRemote().sendText(JSONUtil.toJsonStr(m));
        }
    }

    public static synchronized int getOnlineCount() {
        return onlineNumber;
    }

    public void getNotReadMessages() {
        Set<String> messages = redisTemplate.opsForZSet().range(RedisConstant.MESSAGE_USER_NOT_READ + this.UserId, 0, System.currentTimeMillis());
        if (messages.size() > 0) {
            LeaveMessages leaveMessages=new LeaveMessages();
            leaveMessages.setMessageType("5");
            leaveMessages.setMessages(messages);
            leaveMessages.setSize(messages.size());
            this.session.getAsyncRemote().sendText(JSONUtil.toJsonStr(leaveMessages));
        }
    }
}
